home *** CD-ROM | disk | FTP | other *** search
/ APDL Other Worlds / APDL Other Worlds Collection.iso / SF3000 / Extras / CBlibrary / c / LoadSaveMT < prev    next >
Encoding:
Text File  |  2003-10-16  |  11.3 KB  |  408 lines

  1. /*
  2.  * CBLibrary - LoadSaveMT
  3.  * Copyright (C) 2003  Chris Bazley
  4.  *
  5.  * This library is free software; you can redistribute it and/or
  6.  * modify it under the terms of the GNU Lesser General Public
  7.  * License as published by the Free Software Foundation; either
  8.  * version 2.1 of the License, or (at your option) any later version.
  9.  *
  10.  * This library is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  * Lesser General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU Lesser General Public
  16.  * License along with this library; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  */
  19.  
  20. /* Multi-tasking file loading/saving */
  21.  
  22. /* ANSI library files */
  23. #include <stdlib.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <stdbool.h>
  27. #include <limits.h>
  28.  
  29. /* RISC OS library files */
  30. #include "kernel.h"
  31. #include "flex.h"
  32.  
  33. /* Other headers */
  34. #include "msgtrans.h"
  35. #include "Macros.h"
  36. #include "NoBudge.h"
  37. #include "LoadSaveMT.h"
  38. #include "FopenCount.h"
  39. #include "hourglass.h"
  40.  
  41. #define GRANULARITY 64
  42.  
  43. /*
  44.   This is a workaround for the fact that IO buffering sometimes runs out when accessing files with flex budge (and hence heap expansion) disabled:
  45. */
  46. #define STATIC_BUFFER
  47.  
  48. /*
  49.    The alternative is to pre-expand the heap by an unusually large amount before disabling flex budge:
  50. */
  51. #ifndef STATIC_BUFFER
  52. #define IO_HEAPSPACE 4096
  53. #else
  54. static char static_buffer[GRANULARITY];
  55. #endif
  56.  
  57. typedef struct _fileop_state {
  58.   FILE *f;
  59.   long int read_pos;
  60.   unsigned int mem_pos;
  61.   unsigned int len;
  62. } fileop_state;
  63.  
  64. extern _kernel_oserror shared_err_block;
  65.  
  66. /* ----------------------------------------------------------------------- */
  67. /*                         Public functions                                */
  68.  
  69. unsigned int get_loadsave_perc(FILE ***handle)
  70. {
  71.   fileop_state *state = (fileop_state *)*handle;
  72. #ifndef NDEBUG
  73.   {
  74.     char string[255];
  75.     sprintf(string, "report get_loadsave_perc mem_pos: %d len: %d", state->mem_pos, state->len);
  76.     _kernel_oscli(string);
  77.   }
  78. #endif
  79.   if(state->len == 0)
  80.     return 100; /* guard against divide-by-zero */
  81.  
  82.   if(state->mem_pos < (ULONG_MAX/100))
  83.     return (unsigned int)(state->mem_pos*100) / state->len;
  84.   else
  85.     return (unsigned int)(((float)state->mem_pos*100) / state->len);
  86. }
  87.  
  88. /* ----------------------------------------------------------------------- */
  89.  
  90. _kernel_oserror *load_fileM(const char *filepath, flex_ptr buffer_anchor, const volatile bool *timeup, FILE ***handle, bool sprite)
  91. {
  92.   fileop_state *state;
  93.  
  94.   if(*handle == NULL) {
  95.     /* Starting afresh */
  96. #ifndef NDEBUG
  97.     _kernel_oscli("report starting load");
  98. #endif
  99.  
  100.     state = (fileop_state *)malloc(sizeof(fileop_state));
  101.     if(state == NULL) {
  102.       WRITE_GERR(shared_err_block, "NoMem");
  103.       return &shared_err_block; /* fail */
  104.     }
  105.  
  106.     /* Get size of file */
  107.     {
  108.       _kernel_osfile_block inout;
  109.       if(_kernel_osfile(17, filepath, &inout) == _kernel_ERROR) {
  110.         free(state);
  111.         return _kernel_last_oserror(); /* failure */
  112.       }
  113.       state->len = inout.start;
  114.     }
  115.     if(sprite)
  116.       state->len += sizeof(unsigned int);
  117.  
  118.     /* Allocate buffer for data */
  119.     if(!flex_alloc(buffer_anchor, state->len)) {
  120.       free(state);
  121.       WRITE_GERR(shared_err_block, "NoMem");
  122.       return &shared_err_block; /* fail */
  123.     }
  124.     if(sprite) {
  125.       /* write size of sprite area into the 1st word */
  126.       *(unsigned int *)*buffer_anchor = state->len;
  127.       /* and update write pointer to point past 1st word */
  128.       state->mem_pos = sizeof(unsigned int);
  129.     }
  130.     else
  131.       state->mem_pos = 0;
  132.     state->f = NULL;
  133.     state->read_pos = 0; /* start reading from beginning of file */
  134.  
  135.   } else {
  136.     /* Continue from where we left off */
  137.     state = (fileop_state *)*handle;
  138. #ifndef NDEBUG
  139.     char string[255];
  140.     sprintf(string, "report continuing load from mem_pos %d", state->mem_pos);
  141.     _kernel_oscli(string);
  142. #endif
  143.   }
  144.  
  145.   _kernel_last_oserror(); /* reset SCL's error recording */
  146.  
  147.   if(state->f == NULL) {
  148.     /* (Re)open file */
  149.     state->f = fopen_inc(filepath, "rb"); /* open for reading */
  150.     if(state->f == NULL) {
  151.       free(state);
  152.       *handle = NULL; /* write back NULL pointer */
  153.       THROW(_kernel_last_oserror()) /* any OS error? */
  154.       WRITE_ERR_SUB1(shared_err_block, "OpenInFail", filepath);
  155.       return &shared_err_block; /* fail */
  156.     }
  157.     fseek(state->f, state->read_pos, SEEK_SET); /* start reading data where we left off */
  158.   }
  159.  
  160.   hourglass_on(); /* floppy discs can be real slow! */
  161.   {
  162.     void *ptr;
  163.     unsigned int write_offset = state->mem_pos, len = state->len;
  164.  
  165. #ifdef STATIC_BUFFER
  166.     ptr = static_buffer;
  167. #else
  168.     nobudge_register(IO_HEAPSPACE); /* protect pointer into flexblock */
  169.     ptr = *buffer_anchor;
  170. #endif
  171.  
  172. #ifndef NDEBUG
  173.     if(*timeup)
  174.       _kernel_oscli("report timeup before start");
  175. #endif
  176.  
  177.     /* Read chunks of data until time up */
  178.     do {
  179.       size_t chunk_size;
  180.  
  181.       if(write_offset > len)
  182.         chunk_size = len - write_offset;
  183.       else
  184.         chunk_size = GRANULARITY;
  185.  
  186. #ifdef STATIC_BUFFER
  187.       /* Read chunk from file into static block */
  188.       {
  189.         size_t num_read = fread(ptr, sizeof(char), chunk_size, state->f);
  190.         if(num_read > 0) {
  191.           /* Copy chunk from static block to flex */
  192.           nobudge_register(256);
  193.           memcpy((void *)((unsigned int)*buffer_anchor + write_offset), ptr, num_read);
  194.           nobudge_deregister();
  195.         }
  196.         if(num_read != chunk_size)
  197.           break; /* EOF or read error */
  198.       }
  199. #else
  200.       /* Read chunk from file directly into flex block (budge is disabled) */
  201.       if(fread((void *)((unsigned int)ptr + write_offset), sizeof(char), chunk_size, state->f) != chunk_size)
  202.         break; /* EOF or read error */
  203. #endif
  204.       write_offset += GRANULARITY;
  205.     } while(*timeup == false);
  206.  
  207. #ifndef STATIC_BUFFER
  208.     nobudge_deregister();
  209. #endif
  210.     state->mem_pos = write_offset;
  211.     state->len = len;
  212.   }
  213.   hourglass_off();
  214.  
  215.   if(ferror(state->f)) {
  216.     /* File error on fread() */
  217. #ifndef NDEBUG
  218.     _kernel_oscli("report Aborting - file error on fread()");
  219. #endif
  220.     fclose_dec(state->f);
  221.     free(state);
  222.     *handle = NULL; /* write back NULL pointer */
  223.     THROW(_kernel_last_oserror()) /* any OS error? */
  224.     WRITE_ERR_SUB1(shared_err_block, "ReadFail", filepath);
  225.     return &shared_err_block; /* fail */
  226.   } 
  227.  
  228.   if(feof(state->f)) {
  229.     /* Finished (got to end of input) */
  230. #ifndef NDEBUG
  231.     _kernel_oscli("report Loading complete (EOF)");
  232. #endif
  233.     fclose_dec(state->f);
  234.     free(state);
  235.     *handle = NULL; /* write back NULL pointer */
  236.  
  237.   } else {
  238.     /* Stopped before EOF */
  239. #ifndef NDEBUG
  240.     char string[255];
  241.     sprintf(string, "report Pausing loading at mem_pos %d", state->mem_pos);
  242.     _kernel_oscli(string);
  243. #endif
  244.     if(fopen_num() >= FOPEN_MAX) {
  245.       /* if we have no spare file handles then close file */
  246.       state->read_pos = ftell(state->f);
  247.       fclose_dec(state->f);
  248.       state->f = NULL;
  249.     }
  250.     *handle = (FILE **)state; /* write back pointer to state */
  251.   }
  252.   return NULL; /* no error */
  253. }
  254.  
  255. /* ----------------------------------------------------------------------- */
  256.  
  257. _kernel_oserror *save_fileM(const char *filepath, int filetype, flex_ptr buffer_anchor, const volatile bool *timeup, FILE ***handle, bool sprite)
  258. {
  259.   fileop_state *state;
  260.  
  261.   {
  262.     char *open_mode;
  263.     if(*handle == NULL) {
  264.       /* Starting afresh */
  265. #ifndef NDEBUG
  266.       _kernel_oscli("report starting save");
  267. #endif
  268.       state = (fileop_state *)malloc(sizeof(fileop_state));
  269.       if(state == NULL) {
  270.         WRITE_GERR(shared_err_block, "NoMem");
  271.         return &shared_err_block; /* fail */
  272.       }
  273.       state->len = flex_size(buffer_anchor);
  274.       if(sprite)
  275.          /* skip the 1st word (size of sprite area) */
  276.         state->mem_pos = sizeof(unsigned int);
  277.       else
  278.         state->mem_pos = 0;
  279.  
  280.       state->f = NULL;
  281.       open_mode = "wb"; /* open for writing */
  282.  
  283.     } else {
  284.       /* Continue from where we left off */
  285.       state = (fileop_state *)*handle;
  286. #ifndef NDEBUG
  287.       char string[255];
  288.       sprintf(string, "report continuing saving from mem_pos %d", state->mem_pos);
  289.       _kernel_oscli(string);
  290. #endif
  291.       open_mode = "ab"; /* open for appending */
  292.     }
  293.  
  294.     _kernel_last_oserror(); /* reset SCL's error recording */
  295.  
  296.     if(state->f == NULL) {
  297.       /* (Re)open file */
  298.       state->f = fopen_inc(filepath, open_mode);
  299.       if(state->f == NULL) {
  300.         free(state);
  301.         *handle = NULL; /* write back NULL pointer */
  302.         THROW(_kernel_last_oserror()) /* any OS error? */
  303.         WRITE_ERR_SUB1(shared_err_block, "OpenOutFail", filepath);
  304.         return &shared_err_block; /* fail */
  305.       }
  306.     }
  307.   }
  308.  
  309.   hourglass_on(); /* floppy discs can be real slow! */
  310.   {
  311.     void *ptr;
  312.     unsigned int read_offset = state->mem_pos, len = state->len;
  313.  
  314. #ifdef STATIC_BUFFER
  315.     ptr = static_buffer;
  316. #else
  317.     nobudge_register(IO_HEAPSPACE); /* protect pointer into flexblock */
  318.     ptr = *buffer_anchor;
  319. #endif
  320.  
  321. #ifndef NDEBUG
  322.     if(*timeup)
  323.       _kernel_oscli("report timeup before start");
  324. #endif
  325.  
  326.     /* Write chunks of data until we run out or time up */
  327.     while(read_offset < len) {
  328.       unsigned int chunk_size;
  329.  
  330.       if(read_offset + GRANULARITY > len)
  331.         chunk_size = len - read_offset;
  332.       else
  333.         chunk_size = GRANULARITY;
  334.  
  335. #ifdef STATIC_BUFFER
  336.       /* Copy chunk from flex to static block */
  337.       nobudge_register(256);
  338.       memcpy(ptr, (void *)((unsigned int)*buffer_anchor + read_offset), chunk_size);
  339.       nobudge_deregister();
  340.  
  341.       /* Write contents of static block out to file */
  342.       if(fwrite(ptr, sizeof(char), chunk_size, state->f) != chunk_size)
  343. #else
  344.       /* Write chunk of flex block out to file (budge is disabled) */
  345.       if(fwrite((void *)((unsigned int)ptr + read_offset), sizeof(char), chunk_size, state->f) != chunk_size)
  346. #endif
  347.         break; /* write error */
  348.  
  349.       read_offset += GRANULARITY;
  350.       if(*timeup == true)
  351.         break;
  352.     } /* endwhile */
  353.  
  354.     state->mem_pos = read_offset;
  355.     state->len = len;
  356.  
  357. #ifndef STATIC_BUFFER
  358.     nobudge_deregister();
  359. #endif
  360.   }
  361.   hourglass_off();
  362.  
  363.   if(ferror(state->f)) {
  364. #ifndef NDEBUG
  365.     _kernel_oscli("report Aborting - file error on fwrite()");
  366. #endif
  367.     fclose_dec(state->f);
  368.     free(state);
  369.     *handle = NULL; /* write back NULL pointer */
  370.     THROW(_kernel_last_oserror()) /* any OS error? */
  371.     WRITE_ERR_SUB1(shared_err_block, "WriteFail", filepath);
  372.     return &shared_err_block; /* fail */
  373.   }
  374.  
  375.   if(state->mem_pos >= state->len) {
  376.     /* Finished (got to end of input) */
  377. #ifndef NDEBUG
  378.     _kernel_oscli("report Saving complete");
  379. #endif
  380.     fclose_dec(state->f);
  381.     free(state);
  382.     *handle = NULL; /* write back NULL pointer */
  383.  
  384.     /* Set file type */
  385.     {
  386.       _kernel_osfile_block inout;
  387.       inout.load = filetype;
  388.       if(_kernel_osfile(18, filepath, &inout) == _kernel_ERROR)
  389.         return _kernel_last_oserror(); /* failure */
  390.     }
  391.   } else {
  392.     /* Assume stopped cos of time out */
  393. #ifndef NDEBUG
  394.     char string[255];
  395.     sprintf(string, "report Pausing save at mem_pos %d", state->mem_pos);
  396.     _kernel_oscli(string);
  397. #endif
  398.     if(fopen_num() >= FOPEN_MAX) {
  399.       /* if we have no spare file handles then close file */
  400.       fclose_dec(state->f);
  401.       state->f = NULL;
  402.     }
  403.     *handle = (FILE **)state; /* write back pointer to state */
  404.   }
  405.  
  406.   return NULL; /* success */
  407. }
  408.